﻿using Sustainalytics.ClientDW.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Sustainalytics.ESG.Entities;

namespace Sustainalytics.ReportGenerator.BL
{
    public class ImagesGenerator
    {
        private readonly HistoricalPerformanceChart _historicalPerformanceChartGenerator;

        private readonly IncidentsChart _incidentsChartGenerator;

        private readonly IncidentsMap _incidentsMapGenerator;

        private readonly ControversiesChart _controversiesChartGenerator;


        public static byte[] ImageToByteArray(System.Drawing.Image image)
        {
            var memoryStream = new MemoryStream();
            image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
            return memoryStream.ToArray();
        }

        public static System.Drawing.Image ByteArrayToImage(byte[] buffer)
        {
            var memoryStream = new MemoryStream(buffer);
            var image = System.Drawing.Image.FromStream(memoryStream);
            return image;
        }

        public static byte[] GetPngImage(string imgPath)
        {
            var filePath1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, imgPath);

            var codeBase = Assembly.GetExecutingAssembly().CodeBase;
            var uri = new UriBuilder(codeBase);
            var path = Uri.UnescapeDataString(uri.Path);

            var filePath = Path.GetDirectoryName(path) + imgPath;

            using (var fileStream = File.OpenRead(filePath))
            using (var image = System.Drawing.Image.FromStream(fileStream))
            {
                var memoryStream = new MemoryStream();
                image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
                return memoryStream.ToArray();
            }
        }

        public static byte[] GetJpgImage(string filePath)
        {
            filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath);
            using (var fileStream = File.OpenRead(filePath))
            using (var image = System.Drawing.Image.FromStream(fileStream))
            {
                var memoryStream = new MemoryStream();
                image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                return memoryStream.ToArray();
            }
        }

        public static void SaveImage(string filePath, byte[] picture)
        {
            using (var fileStream = File.OpenWrite(filePath))
            {
                fileStream.Write(picture, 0, picture.Length);
                fileStream.Close();
            }
        }

        public ImagesGenerator()
        {
            _historicalPerformanceChartGenerator = new HistoricalPerformanceChart();
            _incidentsChartGenerator = new IncidentsChart();
            _controversiesChartGenerator = new ControversiesChart();
            _incidentsMapGenerator = new IncidentsMap();
        }

        public byte[] GetSingleIncidentMapImageSmall(List<Location> incidentLocations)
        {
            if (incidentLocations == null) return null;

            _incidentsMapGenerator.Locations = incidentLocations;

            var mapImage = _incidentsMapGenerator.GetMapImageOptimized(800, 400, MapSize.Small);

            return mapImage;
        }


        public byte[] GetIncidentsMapImageBig(List<Location> companyIncidentsLocations, LocationNullable headquarterLocation)
        {
            _incidentsMapGenerator.Locations = companyIncidentsLocations;
            _incidentsMapGenerator.HeadquarterLocation = headquarterLocation;

            return _incidentsMapGenerator.GetMapImageOptimized(1600, 800, MapSize.Big);
        }

        public Dictionary<Guid, byte[]> GetIncidentsChartImages(Dictionary<Guid, List<Incident>> eventIndicatorIncidents)
        {
            var incidentsChartImages = new Dictionary<Guid, byte[]>();

            if (eventIndicatorIncidents == null) return incidentsChartImages;

            foreach (var incidents in eventIndicatorIncidents)
            {
                if (!incidents.Value.Any()) continue;

                var eventIndicatorId = incidents.Key;

                _incidentsChartGenerator.Incidents = GetIncidentsSeriesPoints(incidents.Value);
                _incidentsChartGenerator.MajorIncident = GetMajorIncidentSeriesPoints(incidents.Value);
                
                var chartImage = _incidentsChartGenerator.GetChartImage(940, 235);

                incidentsChartImages.Add(eventIndicatorId, chartImage);
            }

            return incidentsChartImages;
        }

        public SeriesPoints GetIncidentsSeriesPoints(List<Incident> incidents)
        {
            var seriesPoints = new SeriesPoints();

            var xValues = new List<double>();
            var yValues = new List<double>();

            if (incidents != null && incidents.Any())
            {
                xValues = new List<double>();
                yValues = new List<double>();

                foreach (var incident in incidents.OrderBy(i => i.IncidentDate))
                {
                    xValues.Add(incident.IncidentDate.ToOADate());
                    yValues.Add(Math.Max(incident.ImpactScore, incident.RiskScore));
                }
            }

            seriesPoints.XValues = xValues.ToArray();
            seriesPoints.YValues = yValues.ToArray();

            return seriesPoints;
        }

        public SeriesPoints GetMajorIncidentSeriesPoints(List<Incident> incidents)
        {
            var seriesPoints = new SeriesPoints();

            var xValues = new List<double>();
            var yValues = new List<double>();

            var incidentGroups = incidents
                .GroupBy(i => i.IncidentId)
                .Select(group => new
                {
                    IncidentId = group.Key,
                    HighestScore = Math.Max(group.Max(i => i.ImpactScore), group.Max(i => i.RiskScore))
                })
                .ToList();

            if (!incidentGroups.Any()) return seriesPoints;

            var incidentId = incidentGroups.OrderByDescending(ig => ig.HighestScore)
                                           .Select(ig => ig.IncidentId)
                                           .First();

            if (incidentId == null) return seriesPoints;

            var incident = incidents
                .Where(i => i.IncidentId == incidentId)
                .OrderByDescending(i => Math.Max(i.ImpactScore, i.RiskScore))
                .ThenByDescending(i => i.IncidentDate)
                .First();

            if (incident == null) return seriesPoints;

            xValues.Add(incident.IncidentDate.ToOADate());
            yValues.Add(Math.Max(incident.ImpactScore, incident.RiskScore));

            seriesPoints.XValues = xValues.ToArray();
            seriesPoints.YValues = yValues.ToArray();

            return seriesPoints;
        }




        public Dictionary<string, byte[]> GetControversiesChartImages(List<ComparisonGroupControversy> comparisonGroupControversies, List<RPControversyIndicator> controversyIndicators)
        {
            var controversiesChartImages = new Dictionary<string, byte[]>();

            if (comparisonGroupControversies == null || controversyIndicators == null) return controversiesChartImages;

            foreach (var controversyIndicator in controversyIndicators)
            {
                var controversies = comparisonGroupControversies
                    .Where(cgc => cgc.ControversyIndicatorNumber == controversyIndicator.Template.Number)
                    .ToList();

                _controversiesChartGenerator.Controversies = GetControversiesSeriesPoints(controversyIndicator, controversies);
                
                var chartImage = _controversiesChartGenerator.GetChartImage(300, 200);

                controversiesChartImages.Add(controversyIndicator.Template.Number, chartImage);
            }

            return controversiesChartImages;
        }


        private static SeriesPoints GetControversiesSeriesPoints(RPControversyIndicator controversyIndicator, List<ComparisonGroupControversy> controversies)
        {
            var xValues = new double[] { 0, 1, 2, 3, 4, 5 };
            var yValues = new double[] { 0, 0, 0, 0, 0, 0 };
            var axisLabels = new[] { "None", "Category 1", "Category 2", "Category 3", "Category 4", "Category 5" };
            double xValue = -1;

            if (controversies != null && controversies.Any())
            {
                foreach (var controvery in controversies)
                {
                    yValues[controvery.Category] = controvery.Percent;
                }
            }

            if (controversyIndicator != null)
            {
                xValue = controversyIndicator.AnswerCategory;
            }

            var controversiesSeriesPoints = new SeriesPoints
            {
                XValues = xValues,
                XValue = xValue,
                YValues = yValues,
                AxisLabels = axisLabels
            };

            return controversiesSeriesPoints;
        }

        public Dictionary<ThemeEnum, byte[]> GetHistoricalPerformanceChartImages(List<CompanyHistoryData> companyHistoricalPerformance)
        {
            var historicalPerformanceChartImages = new Dictionary<ThemeEnum, byte[]>();

            _historicalPerformanceChartGenerator.Calibration = false;
            _historicalPerformanceChartGenerator.BestInClass = GetSeriesPoints(ThemeEnum.Company, ScoreType.BestInClass, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.CompanyScores = GetSeriesPoints(ThemeEnum.Company, ScoreType.Score, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.IndustryAverage = GetSeriesPoints(ThemeEnum.Company, ScoreType.Average, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.LargeChart = true;

            var historicalPerformanceChartImage = _historicalPerformanceChartGenerator.GetChartImage(1350, 220);
            historicalPerformanceChartImages.Add(ThemeEnum.Company, historicalPerformanceChartImage);


            _historicalPerformanceChartGenerator.Calibration = false;
            _historicalPerformanceChartGenerator.BestInClass = GetSeriesPoints(ThemeEnum.Environment, ScoreType.BestInClass, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.CompanyScores = GetSeriesPoints(ThemeEnum.Environment, ScoreType.Score, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.IndustryAverage = GetSeriesPoints(ThemeEnum.Environment, ScoreType.Average, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.LargeChart = false;
            _historicalPerformanceChartGenerator.ShowArea1AxisY = false;

            var environmentHistoricalPerformanceChartImage = _historicalPerformanceChartGenerator.GetChartImage(420, 220);
            historicalPerformanceChartImages.Add(ThemeEnum.Environment, environmentHistoricalPerformanceChartImage);

            _historicalPerformanceChartGenerator.Calibration = false;
            _historicalPerformanceChartGenerator.BestInClass = GetSeriesPoints(ThemeEnum.Social, ScoreType.BestInClass, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.CompanyScores = GetSeriesPoints(ThemeEnum.Social, ScoreType.Score, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.IndustryAverage = GetSeriesPoints(ThemeEnum.Social, ScoreType.Average, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.ShowArea1AxisY = false;
            _historicalPerformanceChartGenerator.ShowArea2AxisY = false;

            var socialHistoricalPerformanceChartImage = _historicalPerformanceChartGenerator.GetChartImage(420, 220);
            historicalPerformanceChartImages.Add(ThemeEnum.Social, socialHistoricalPerformanceChartImage);

            _historicalPerformanceChartGenerator.Calibration = false;
            _historicalPerformanceChartGenerator.BestInClass = GetSeriesPoints(ThemeEnum.Governance, ScoreType.BestInClass, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.CompanyScores = GetSeriesPoints(ThemeEnum.Governance, ScoreType.Score, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.IndustryAverage = GetSeriesPoints(ThemeEnum.Governance, ScoreType.Average, companyHistoricalPerformance);
            _historicalPerformanceChartGenerator.ShowArea1AxisY = true;
            _historicalPerformanceChartGenerator.ShowArea2AxisY = false;

            var governanceHistoricalPerformanceChartImage = _historicalPerformanceChartGenerator.GetChartImage(420, 220);

            historicalPerformanceChartImages.Add(ThemeEnum.Governance, governanceHistoricalPerformanceChartImage);

            return historicalPerformanceChartImages;
        }

        private static SeriesPoints GetSeriesPoints(ThemeEnum theme, ScoreType scoreType, List<CompanyHistoryData> companyHistoricalPerformance)
        {
            if (companyHistoricalPerformance == null || !companyHistoricalPerformance.Any())
            {
                return new SeriesPoints
                {
                    XValues = new[] { 0.0 },
                    YValues = new[] { 0.0 },
                };
            }

            var xValues = new List<double>();
            var yValues = new List<double>();

            foreach (var chp in companyHistoricalPerformance)
            {
                if (theme == ThemeEnum.Company && scoreType == ScoreType.BestInClass )
                {
                    if (chp.OverallBestInClass > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.OverallBestInClass);
                    }
                }
                if (theme == ThemeEnum.Company && scoreType == ScoreType.Score)
                {
                    if (chp.OverallScore > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.OverallScore);
                    }
                }
                if (theme == ThemeEnum.Company && scoreType == ScoreType.Average)
                {
                    if (chp.OverallAverage > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.OverallAverage);
                    }
                }

                if (theme == ThemeEnum.Environment && scoreType == ScoreType.BestInClass)
                {
                    if (chp.EnvironmentBestInClass > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.EnvironmentBestInClass);
                    }
                }
                if (theme == ThemeEnum.Environment && scoreType == ScoreType.Score)
                {
                    if (chp.EnvironmentScore > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.EnvironmentScore);
                    }
                }
                if (theme == ThemeEnum.Environment && scoreType == ScoreType.Average)
                {
                    if (chp.EnvironmentAverage > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.EnvironmentAverage);
                    }
                }

                if (theme == ThemeEnum.Social && scoreType == ScoreType.BestInClass)
                {
                    if (chp.SocialBestInClass > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.SocialBestInClass);
                    }
                }
                if (theme == ThemeEnum.Social && scoreType == ScoreType.Score)
                {
                    if (chp.SocialScore > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.SocialScore);
                    }
                }
                if (theme == ThemeEnum.Social && scoreType == ScoreType.Average)
                {
                    if (chp.SocialAverage > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.SocialAverage);
                    }
                }

                if (theme == ThemeEnum.Governance && scoreType == ScoreType.BestInClass)
                {
                    if (chp.GovernanceBestInClass > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.GovernanceBestInClass);
                    }
                }
                if (theme == ThemeEnum.Governance && scoreType == ScoreType.Score)
                {
                    if (chp.GovernanceScore > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.GovernanceScore);
                    }
                }
                if (theme == ThemeEnum.Governance && scoreType == ScoreType.Average)
                {
                    if (chp.GovernanceAverage > 0)
                    {
                        xValues.Add(chp.Date.ToOADate());
                        yValues.Add(chp.GovernanceAverage);
                    }
                }
            }

            return new SeriesPoints
            {
                XValues = xValues.ToArray(),
                YValues = yValues.ToArray()
            };
        }

    }

    public enum ThemeEnum
    {
        Company = 0,
        Environment = 1,
        Social = 2,
        Governance = 3
    }

    public enum ScoreType
    {
        BestInClass = 1,
        Score = 2,
        Average = 3
    }
}
